﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Security.Permissions;
using System.Web.UI;
using System.Web.UI.WebControls;
using Medianamik.Core;
using Medianamik.Core.Interfaces;
using Medianamik.Core.Security;
using Medianamik.Core.Sugar;
using Medianamik.UI.Controls;
using Telerik.Web.UI;
using Roles = Medianamik.Core.Security.Roles;
using Medianamik.Core.Querying;

namespace Medianamik.UI.NodeManagement
{
    public partial class NodeList : BasePage
    {

        const string PostBackEventTarget = "__EVENTTARGET";
        const string PostBackEventArgument = "__EVENTARGUMENT";



        protected void Page_Init(object sender, EventArgs e)
        {
            var cultureSelectorWindow = new BaseModalWindow { ID = "CultureSelectorWindow" };
            var typeSelectorWindow = new BaseModalWindow { ID = "TypeSelectorWindow" };
            var deleteNodeWindow = new BaseModalWindow { ID = "deleteNodeWindow" };

            if (!Page.IsPostBack)
            {
                typeSelectorWindow.OnClientClose = "onNodeTypeSelected";
                typeSelectorWindow.IconUrl = "/Medianamik/images/Icones/Ico_AjoutPage.gif";
                typeSelectorWindow.ReloadOnShow = true;
                typeSelectorWindow.Width = Unit.Pixel(400);
                typeSelectorWindow.Height = Unit.Pixel(200);
                typeSelectorWindow.Behaviors = WindowBehaviors.Close;

                cultureSelectorWindow.OnClientClose = "onCultureSelected";
                cultureSelectorWindow.IconUrl = "/Medianamik/images/Icones/Ico_AjoutPage.gif";
                cultureSelectorWindow.ReloadOnShow = true;
                cultureSelectorWindow.Width = Unit.Pixel(400);
                cultureSelectorWindow.Height = Unit.Pixel(200);

                deleteNodeWindow.OnClientClose = "deleteNodeWindowCallback";
                deleteNodeWindow.IconUrl = "/Medianamik/images/Icones/Ico_Supprimer.gif";
                deleteNodeWindow.ReloadOnShow = true;
                deleteNodeWindow.Width = Unit.Pixel(400);
                deleteNodeWindow.Height = Unit.Pixel(200);
            }

            MyWindowManager.Windows.Add(deleteNodeWindow);
            MyWindowManager.Windows.Add(typeSelectorWindow);
            MyWindowManager.Windows.Add(cultureSelectorWindow);
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            Response.AppendHeader("Cache-Control", "no-cache; private; no-store; must-revalidate; max-stale=0; post-check=0; pre-check=0; max-age=0");
            RadAjaxManager.GetCurrent(this).AjaxRequest += NodeList_AjaxRequest;
            bctFilAriane.NavigateParent += bctFilAriane_NavigateParent;
            lnkAddNode.Attributes.Add("onClick", "GetRadWindowManager().Open(null, 'TypeSelectorWindow');");

            if (IsPostBack)
            {
                //Un noeud de l'abre "ne requérant pas de chargement des enfants" vient d'être sélectionné (NodeClick)
                if (!String.IsNullOrEmpty(Request[PostBackEventTarget])
                    && Request[PostBackEventTarget].Equals(isNavigating.ClientID)
                    && !String.IsNullOrEmpty(Request[PostBackEventArgument]))
                {
                    AddHistoryPoint(Request[PostBackEventArgument]);
                    ParentNodeId = new Guid(Request[PostBackEventArgument]);
                    GoToParentNodeId();
                    LoadBreadcrumbs();
                    grdNodes.Rebind();
                    SetRadWindowUrl();
                    breadcrumbUpdatePanel.Update();
                    addNodeUpdatePanel.Update();
                    gridUpdatePanel.Update();
                }
            }
            else
            {
                if (!Roles.UserHasRight(ParentNode, SecurityLevels.Read))
                    Response.Redirect("/medianamik/Unauthorized.aspx");

                LoadRootNodes();
                LoadBreadcrumbs();
                SetRadWindowUrl();
            }
        }

        protected override void OnCultureChanged(CultureChangedEventArgs e)
        {
            grdNodes.Rebind();
            gridUpdatePanel.Update();
            ReloadTree();
            LoadBreadcrumbs();
            breadcrumbUpdatePanel.Update();
        }

        [PrincipalPermission(SecurityAction.Assert, Role = "ElementActivationManagement ")]
        protected void OnNodeActivation(object sender, CommandEventArgs e)
        {
            NodeManager.SetNodeActiveStatus(new Guid(e.CommandArgument.ToString()),
                                            CurrentEditionCulture,
                                            e.CommandName.ToLowerInvariant() == "activate");

            grdNodes.Rebind();
        }


        protected void OnNavigate(object sender, HistoryEventArgs e)
        {
            if (MyScriptManager.IsNavigating)
            {
                string parentNodeId = e.State.Get("ParentNodeId");

                if (parentNodeId == null)
                {
                    ParentNodeId = null;
                    parentNodeId = ParentNodeId.GetValueOrDefault(RootNodeId).ToString();
                }
                else
                {
                    ParentNodeId = new Guid(parentNodeId);
                }

                GoToParentNodeId();
                LoadBreadcrumbs();
                grdNodes.Rebind();
                SetRadWindowUrl();
                breadcrumbUpdatePanel.Update();
                addNodeUpdatePanel.Update();
                gridUpdatePanel.Update();
            }
        }

        void bctFilAriane_NavigateParent(object sender, NavigateParentEventArgs e)
        {
            ParentNodeId = new Guid(e.ItemID);
            GoToParentNodeId();
            LoadBreadcrumbs();
            grdNodes.Rebind();
            SetRadWindowUrl();
            gridUpdatePanel.Update();
            addNodeUpdatePanel.Update();
        }

        protected void NodeExpand(object sender, RadTreeNodeEventArgs e)
        {
            if (String.IsNullOrEmpty(isNavigating.Value))
            {
                ExpandNode(e.Node, false);
            }
            else
            {
                ParentNodeId = new Guid(isNavigating.Value);
                AddHistoryPoint(isNavigating.Value);
                isNavigating.Value = "";
                ExpandNode(e.Node, true);
                LoadBreadcrumbs();
                grdNodes.Rebind();
                SetRadWindowUrl();
                breadcrumbUpdatePanel.Update();
                addNodeUpdatePanel.Update();
                gridUpdatePanel.Update();
            }
        }

        protected void grdNodes_ItemCommand(object source, GridCommandEventArgs e)
        {
            if (e.CommandName == String.Empty)
            {
                var item = e.Item as GridDataItem;
                if (item != null)
                {
                    ParentNodeId = (Guid)item.OwnerTableView.DataKeyValues[e.Item.ItemIndex]["ID"];
                    GoToParentNodeId();
                    LoadBreadcrumbs();
                    grdNodes.Rebind();
                    SetRadWindowUrl();

                    breadcrumbUpdatePanel.Update();
                    addNodeUpdatePanel.Update();

                    if (MyScriptManager.IsInAsyncPostBack && (MyScriptManager.IsNavigating != true))
                    {
                        AddHistoryPoint(ParentNodeId.Value.ToString());
                    }
                }
            }
        }

        protected void grdNodes_ItemDataBound(object sender, GridItemEventArgs e)
        {
            //Trace.Write("Function call start");

            if (e.Item.ItemType == GridItemType.Item || e.Item.ItemType == GridItemType.AlternatingItem)
            {
                var gridItem = (GridDataItem)e.Item;

                if (gridItem.DataItem != null)
                {
                    var node = (Node)gridItem.DataItem;

                    var editButton = (LockButton)e.Item.FindControl("EditButton");
                    EditButtonConfigurator.Configure(editButton, node);

                    var securityPlaceHolder = (PlaceHolder)e.Item.FindControl("SecurityPlaceHolder");
                    if (securityPlaceHolder != null && !Roles.IsUserInRole(MedianamikRoles.ManageSecurity))
                        securityPlaceHolder.Visible = false;

                    var nodeActivationButton = e.Item.FindControl("btnNodeActivation") as NodeActivationButton;
                    if (nodeActivationButton != null && !Roles.IsUserInRole(MedianamikRoles.ElementActivationManagement))
                        nodeActivationButton.Enabled = false;

                    var link = (LinkButton)e.Item.FindControl("lnkNodeName");
                    if (link != null)
                        link.Enabled = node.CanHaveChildNodes && Roles.UserHasRight(node, SecurityLevels.Read);// && node.CanHaveLegitimateChildNodes;

                    var diffLinkButton = e.Item.FindControl("DiffLinkButton") as DiffLink;
                    if (diffLinkButton != null)
                        diffLinkButton.Enabled = diffLinkButton.Enabled &&
                                                 node.Instances.Exists(CurrentEditionCulture.Name);

                    var revisionButton = e.Item.FindControl("RevisionButton") as LockButton;
                    if (revisionButton != null)
                        revisionButton.Enabled = revisionButton.Enabled &&
                                                 node.Instances.Exists(CurrentEditionCulture.Name)
                                                 && node.NodeType.IsVersioned;
                    //Roles.UserHasRight(node, SecurityLevels.Update); pas besoin puisque appliqué au bouton

                    var deleteButton = (WebControl)e.Item.FindControl("deleteNodeButton");

                    deleteButton.Enabled = deleteButton.Enabled //&& Roles.UserHasRight(node, SecurityLevels.Delete) pas besoin puisque appliqué au bouton
                        && (node.Instances.Exists(CurrentEditionCulture.Name) || MedianamikContext.Current.MedianamikUser.CanDeleteAllInstancesOfANode)
                        && (!node.HasChildren || MedianamikContext.Current.MedianamikUser.CanDeleteNodeBranch);

                    if (node.IsDraft && node.IsVersioned)
                        gridItem.CssClass = "rgRow ElementBrouillon";

                    var btnNodeActivation = (NodeActivationButton)e.Item.FindControl("btnNodeActivation");

                    var btnEditSecurity = e.Item.FindControl("btnEditSecurity") as LockButton;

                    if (!node.HasInstanceForCurrentEditionCulture)
                    {
                        gridItem.CssClass = "rgRow ElementNonTraduit";
                        btnNodeActivation.Enabled = false;
                        btnEditSecurity.Enabled = false;
                    }
                    else
                    {
                        btnNodeActivation.Enabled = btnNodeActivation.Enabled && true;
                    }
                }
            }

            //Trace.Write("Function call end");
        }

        protected void grdNodes_NeedDataSource(object source, GridNeedDataSourceEventArgs e)
        {
            grdNodes.DataSource = ChildNodes;
            var childNodeCount = new Query().Where(n => n.ParentID, Comparison.Equal, ParentNode.ID).Count();
            grdNodes.VirtualItemCount = childNodeCount + childNodeCount / 20 + 1;
        }

        protected void grdNodes_PageIndexChanged(object source, GridPageChangedEventArgs e)
        {
            _childNodes = null;
            grdNodes.Rebind();

        }

        protected void grdNodes_RowDrop(object sender, GridDragDropEventArgs e)
        {
            if (e.DraggedItems[0].OwnerGridID == grdNodes.ClientID)
            {
                Node movedNode = NodeManager.GetNode((Guid)e.DraggedItems[0].GetDataKeyValue("ID"));

                if (movedNode != null)
                {

                    //TODO: Plus compliqué que ça pour le réordonnement... peut-il modifier les noeuds qui seront implicitement déplacés
                    if (Roles.UserHasRight(movedNode, SecurityLevels.Update))
                    {
                        Guid destinationNodeId;
                        //Réordonner un noeud
                        if (string.IsNullOrEmpty(e.HtmlElement))
                        {

                            destinationNodeId = (Guid)e.DestDataItem.GetDataKeyValue("ID");

                            DropPosition pos = e.DropPosition == GridItemDropPosition.Above
                                ? DropPosition.Above
                                : DropPosition.Below;

                            NodeManager.ChangeNodeOrder(movedNode, destinationNodeId, pos);
                            grdNodes.Rebind();
                            ReloadCurrentTreeNode();
                        } //Déplacer un noeud
                        else if (StringHelper.IsGuid(TreeNodeId.Value, out destinationNodeId))
                        {
                            if (UserMayMoveNode)
                            {
                                if (movedNode.MayBeMoveTo(destinationNodeId))
                                {
                                    Guid ParentIdBeforeMoved = movedNode.ParentID.Value;

                                    MoveNode(destinationNodeId, movedNode);
                                    ReloadBranchTreeNode(ParentIdBeforeMoved);
                                    ReloadBranchTreeNode(movedNode.ParentID.Value);
                                    grdNodes.Rebind();
                                    gridUpdatePanel.Update();
                                    GoToParentNodeId();
                                    var parentNode = treeview.FindNodeByValue(movedNode.ParentID.Value.ToString());
                                    parentNode.ExpandMode = TreeNodeExpandMode.ServerSide;
                                    ExpandNode(parentNode, false);
                                }
                                else
                                {
                                    Alert(Resources("Medianamik_ContentManagement", "MoveNodeToWrongDestination"));
                                }
                            }
                            else
                            {
                                Alert(Resources("Medianamik_ContentManagement", "UserMayMoveNodeAlert"));
                            }
                        }
                    }
                    else
                    {
                        //TODO: Ajouter la possibilité d'afficher un message à partir du BasePage de Medianamik
                        Alert(Resources("Medianamik_ContentManagement", "UserMayMoveNodePermissionAlert"));
                    }
                }
            }
        }

        public void NodeList_AjaxRequest(object sender, AjaxRequestEventArgs e) //Effacer
        {
            grdNodes.Rebind();
            ReloadCurrentTreeNode();
            gridUpdatePanel.Update();
        }

        protected void LockLink_RequestFailed(object sender, CommandEventArgs e)
        {
            grdNodes.Rebind();
            LockInfoTooltip.ObjectId = new Guid((string)e.CommandArgument);
            LockInfoTooltip.DataBind();
            LockInfoTooltip.Show();
        }

        protected void LockInfoTooltip_LockDeleted(object sender, EventArgs e)
        {
            //TODO : Rediriger vers le noeud dévérouillé (besoin dans EventArgs)
            //grdNodes.Rebind();
            //gridUpdatePanel.Update();
        }

        private Guid? _parentNodeId;
        public Guid? ParentNodeId
        {
            get
            {
                if (_parentNodeId == Guid.Empty)
                    return null;

                if (ViewState["ParentNodeId"] == null)
                {
                    string strParentNodeId = Request.QueryString[DefaultParamNames.ParentNodeID];

                    if (!String.IsNullOrEmpty(strParentNodeId) && !StringHelper.IsGuid(strParentNodeId, ref _parentNodeId))
                    {
                        Response.Redirect(ReturnURL);
                    }
                    else
                    {
                        ViewState["ParentNodeId"] = _parentNodeId;
                    }
                }
                else
                {
                    _parentNodeId = (Guid)ViewState["ParentNodeId"];
                }

                return _parentNodeId;
            }
            set
            {
                ViewState["ParentNodeId"] = value == Guid.Empty
                                          ? null
                                          : value;

                _parentNodeId = value;
                _parentNode = null;
            }
        }


        protected bool UserMayMoveNode
        {
            get
            {
                return User.IsInRole("MoveNode");
            }
        }

        private List<Node> _childNodes;
        public List<Node> ChildNodes
        {
            get
            {
                if (_childNodes == null)
                {
                    _childNodes = new Query()
                    .Where(n => n.ParentID, Comparison.Equal, ParentNode.ID)
                    .SetFetchMode(FetchMode.LazyLoadProperties )
                    .Execute()
                    .Where(n => !n.NodeType.IsSystem && !n.NodeType.Listable)
                    .Where(n => Roles.UserHasRight(n, SecurityLevels.Read)).ToList();
                }
                return _childNodes;
            }
        }

        private Node _parentNode;
        public Node ParentNode
        {
            get
            {
                if (_parentNode == null)
                {
                    _parentNode = ParentNodeId.HasValue
                                ? NodeManager.GetNode(ParentNodeId.Value)
                                : NodeManager.GetNode(RootNodeId);
                    if (_parentNode != null)
                        _parentNode.DefaultCulture = CurrentEditionCulture;
                    else
                        Response.Redirect("NodeList.aspx");
                }

                return _parentNode;
            }
        }

        private string _addURL;
        public string AddURL
        {
            get
            {
                if (_addURL == null)
                {
                    _addURL = "Nodemanagement.aspx" + GetParentNodeParam("?");
                }

                return _addURL;
            }
        }

        private string _returnURL;
        public string ReturnURL
        {
            get
            {
                if (_returnURL == null)
                {
                    _returnURL = "NodeList.aspx";

                    if (ParentNode.ParentID.HasValue)
                        _returnURL += "?" + DefaultParamNames.ParentNodeID + "=" + ParentNode.ParentID.Value;
                }

                return _returnURL;
            }
        }

        protected Guid RootNodeId
        {
            get { return MedianamikModules.ContentManagement; }
        }

        private EditButtonConfigurator _editButtonConfigurator;
        protected EditButtonConfigurator EditButtonConfigurator
        {
            get
            {
                return _editButtonConfigurator
                    ?? (_editButtonConfigurator = new EditButtonConfigurator(ParentNode.WebSite, CurrentEditionCulture));
            }
        }




        public void ReloadTree()
        {
            treeview.Nodes.Clear();
            GoToParentNodeId();
        }

        private void ExpandNode(RadTreeNode node, bool selected)
        {
            ExpandNode(node, selected, null);
        }

        private void ExpandNode(RadTreeNode node, bool selected, Node rootNode)
        {
            if (selected)
            {
                treeview.ClearSelectedNodes();
                //treeview.CollapseAllNodes();
            }

            if (node == null)
                throw new ApplicationException("The parent node is not part of the hierarchy.");
            
            if (node.ExpandMode == TreeNodeExpandMode.ServerSide)
            {
                var parentNode = rootNode ?? NodeManager.GetNode(new Guid(node.Value));

                if (parentNode == ParentNode)
                {
                    foreach (var item in ChildNodes)
                    {
                        var newNode = CreateTreeNode(item);

                        if (item.HasChildren)
                        {
                            newNode.ExpandMode = TreeNodeExpandMode.ServerSide;
                        }

                        node.Nodes.Add(newNode);
                    }
                }
                else
                {
                    foreach (var item in parentNode.GetChildContentNodes(CurrentEditionCulture).Where(n => !n.NodeType.IsSystem))
                    {
                        var newNode = CreateTreeNode(item);

                        if (item.HasChildren)
                        {
                            newNode.ExpandMode = TreeNodeExpandMode.ServerSide;
                        }

                        node.Nodes.Add(newNode);
                    }
                }

                node.ExpandMode = TreeNodeExpandMode.ClientSide;
            }

            node.Expanded = true;
            node.ExpandParentNodes();
            node.Selected = selected;
        }

        private void LoadRootNodes()
        {
            Node rootNode = NodeManager.GetNode(RootNodeId);
            RadTreeNode rootTreeNode = CreateTreeNode(rootNode);
            rootTreeNode.ExpandMode = TreeNodeExpandMode.ServerSide;
            treeview.Nodes.Add(rootTreeNode);
            ExpandNode(rootTreeNode, true, rootNode);
            if (ParentNode.ID != RootNodeId)
            {
                GoToParentNodeId();
            }
        }

        private void GoToParentNodeId()
        {
            if (treeview.Nodes.Count < 1)
                LoadRootNodes();

            if (treeview.Nodes.Count > 0)
            {
                var selectedTreeNode = treeview.FindNodeByValue(ParentNodeId.GetValueOrDefault(RootNodeId).ToString());

                //Si le noeud n'est pas dans l'arbre
                if (selectedTreeNode == null)
                {
                    //Trouver le parent déjà dans l'arbre
                    var parentIDs = ParentNode.GetParentIDs(true);
                    var i = 0;

                    while (selectedTreeNode == null && i < parentIDs.Count())
                    {
                        selectedTreeNode = treeview.FindNodeByValue(parentIDs.ElementAt(i).ToString());
                        i++;
                    }

                    //Expand parent node until SelectedNodeId reached
                    for (var j = i - 1; j >= 0; j--)
                    {
                        ExpandNode(selectedTreeNode, false);

                        if (j - 1 > 0)
                        {
                            selectedTreeNode = selectedTreeNode.Nodes
                                .FindNodeByValue(parentIDs.ElementAt(j - 1).ToString());
                        }
                    }

                    selectedTreeNode = selectedTreeNode.Nodes.FindNodeByValue(ParentNodeId.Value.ToString());
                }

                ExpandNode(selectedTreeNode, true, ParentNode);
            }
        }

        private static RadTreeNode CreateTreeNode(INode node)
        {
            return new RadTreeNode
                       {
                           Text = node.DisplayName,
                           Value = node.ID.ToString(),
                           ImageUrl = node.NodeType.IconPath ?? String.Empty
                       };
        }



        //Dépendemment du contexte, nous ne voulons pas afficher le lien Ajouter un élément
        private void SetAddNodeVisibility()
        {
           var canAddElement = ParentNode.CanHaveChildNodes && 
                               ParentNode.CanHaveLegitimateChildNodes;

           lnkAddNode.CssClass = lnkAddNode.CssClass.Replace("-Inactif", "");

           if (!canAddElement)               
           {
               lnkAddNode.CssClass += "-Inactif";
               lnkAddNode.Attributes.Remove("onClick");
               lnkAddNode.NavigateUrl = "javascript:void(0);";
           }


           addNodeUpdatePanel.Update();
        }

        private void LoadBreadcrumbs()
        {
            var bc = ParentNode.Breadcrumbs.ToList();
            if (bc.Count() > 0)
            {
                bc[0] = new Breadcrumb(RootNodeId.ToString(), NodeManager.GetNode(RootNodeId).Instances[CurrentUICulture].Name);
            }
            bctFilAriane.Breadcrumbs = bc;
            bctFilAriane.DataBind();
        }

        [PrincipalPermission(SecurityAction.Assert, Role = "PublishContent")]
        private static void MoveNode(Guid destinationNodeId, Node movedNode)
        {
            NodeManager.MoveNode(movedNode, destinationNodeId);
        }

        private void AddHistoryPoint(string historyPoint)
        {
            var pageState = new NameValueCollection { { "ParentNodeId", historyPoint } };
            MyScriptManager.AddHistoryPoint(pageState, Page.Title);
        }

        private void SetRadWindowUrl()
        {
            lnkAddNode.NavigateUrl = String.Format("/medianamik/modalwindows/typeselectorwindow.aspx{0}",
                GetParentNodeParam("?"));
            SetAddNodeVisibility();
        }

        protected string GetParentNodeParam(string pParamDelimiter)
        {
            return String.Format("{0}{2}={1}", pParamDelimiter,
                    ParentNode.ID, DefaultParamNames.ParentNodeID);
        }

        protected Guid GetPreviewNodeId(Node pNode)
        {
            if (pNode.NodeTypeID == MedianamikTypes.MedianamikSyndication)
            {
                return pNode.DefaultInstance[MedianamikProperties.SyndicatedNode].GetValue<Guid?>() ?? Guid.Empty;
            }
            return pNode.ID;
        }

        protected override void OnPreInit(EventArgs e)
        {
            base.OnPreInit(e);
            if (Request.Browser.MSDomVersion.Major == 0) // If it is Non IE Browser
            {
                Response.Cache.SetNoStore();
            }
        }

        private void ReloadCurrentTreeNode()
        {
            RadTreeNode currentNode = treeview.SelectedNode;
            currentNode.ExpandMode = TreeNodeExpandMode.ServerSide;
            currentNode.Nodes.Clear();
            GoToParentNodeId();
        }

        private void ReloadBranchTreeNode(Guid parentId)
        {
            var parentNode = treeview.FindNodeByValue(parentId.ToString(), true);
            if (parentNode != null)
            {
                parentNode.ExpandMode = TreeNodeExpandMode.ServerSide;
                parentNode.Nodes.Clear();
            }
        }

    }
}
